# -*- coding: utf-8 -*-
"""
    moe.answers.models
    ~~~~~~~~~~~~~~~~~~

    Q&A models.

    Initial concept based on Solace: http://bitbucket.org/plurk/solace

    :copyright: 2009 by tipfy.org.
    :license: BSD, see LICENSE.txt for more details.
"""
import datetime

from google.appengine.api import datastore_errors
from google.appengine.ext import db

from werkzeug import cached_property

from tipfy.appengine.db import KeyProperty, get_entity_dict, populate_entity

from moe.models import DateMixin, ArticleRevisionModel
from moe.models import User
"""
# Queries needed.

# By number of votes.
Question.all().order('-votes')

# By author.
Question.all().filter('author_key', 'foo')

# By unanswered.
Question.all().filter('unanswered', True)


class AnswersTopicRevision(ArticleRevisionModel):
    # XXX
    # question_post_id INTEGER,
    # XXX
    # answer_post_id INTEGER,
    # XXX
    # author_id INTEGER,

    # XXX
    # answer_author_id INTEGER,
"""
class QuestionRevision(ArticleRevisionModel):
    @classmethod
    def get_key(cls, question_id, version=None):
        parts = [
            Question.kind(), question_id,
            cls.kind(), '__latest__',
        ]
        if version is not None:
            # This is not the latest revision.
            parts += [cls.kind(), version]

        return db.Key.from_path(*parts)

    @classmethod
    def get_by_question_id(cls, question_id, version=None):
        return cls.get(cls.get_key(question_id, version))


class Question(db.Model, DateMixin):
    slug = db.StringProperty()
    tags = db.StringListProperty()
    author_key = KeyProperty()

    # Properties that are not versioned.
    # Status code:
    # 0: awaiting moderation, 1: active, 2: deleted
    status = db.IntegerProperty(default=1)
    # Number of upvotes.
    votes = db.IntegerProperty(default=0)
    # Number of answers.
    answers = db.IntegerProperty(default=0)
    #
    hotness = db.FloatProperty(default=0.0)

    # Choosen answer.
    answered = db.BooleanProperty(default=False)
    answer_date = db.DateTimeProperty()
    answer_key = KeyProperty()

    @cached_property
    def author(self):
        # XXX use global cache
        if self.author_key:
            return User.get(self.author_key)

    @cached_property
    def latest_revision(self):
        return QuestionRevision.get_by_question_id(self.key().id())

    @classmethod
    def insert_or_update(self, question, question_values, revision_values):
        now = datetime.datetime.now()
        revision_values.update({
            'created': now,
            'updated': now,
            'is_root': True,
        })

        def txn(question):
            if question is None:
                question = Question(**question_values)
            else:
                populate_entity(question, **question_values)

            question_key = question.put()
            new_revision = QuestionRevision(key_name='__latest__',
                parent=question, **revision_values)
            entities = [new_revision]

            # Check if we need to save old revision in the revision history.
            old_revision = QuestionRevision.get_by_key_name('__latest__',
                parent=question)
            if old_revision is not None:
                # Save old data in the revision history with latest revision
                # as parent.
                old_data = get_entity_dict(old_revision)
                old_data['is_root'] = False
                entities.append(QuestionRevision(parent=new_revision,
                    **old_data))

            # XXX set a transactional task.
            # taskqueue.add(url='/path/to/my/worker', transactional=True)
            res = list(db.put(entities))
            res.insert(0, question_key)
            return res

        try:
            return db.run_in_transaction(txn, question)
        except datastore_errors.TransactionFailedError, e:
            logging.debug(e)
            return None

    def _set_answered(self, answer=None, valid=True):
        if self.answered == valid:
            # Nothing changed.
            return

        now = datetime.datetime.now()
        self.updated = now
        self.answered = valid
        if valid:
            self.answer_date = now
            self.answer_key = answer
        else:
            self.answer_date = None
            self.answer_key = None

        self.put()

    def set_answered(self, answer):
        self._set_answered(answer=answer, valid=True)

    def set_unanswered(self):
        self._set_answered(valid=False)


def question_pager(cursor=None, filters=None, orders=None, limit=20):
    def get_query(keys_only=False, cursor=None):
        query = Question.all(keys_only=keys_only)

        if filters:
            for filter_prop, filter_value in filters:
                query.filter(filter_prop, filter_value)

        if orders:
            for order_prop in orders:
                query.order(order_prop)

        if cursor is not None:
            query.with_cursor(cursor)

        return query

    query = get_query(cursor=cursor)
    entities = query.fetch(limit)

    if not entities:
        query_cursor = None
    else:
        query_cursor = query.cursor()

        # Check if we have "next" results.
        res = get_query(keys_only=True, cursor=query_cursor).get()
        if res is None:
            query_cursor = None

    return entities, query_cursor


def question_pager_by_tag(tags, cursor=None, limit=20):
    """
    # By tag.
    Question.all().filter('tag', 'foo').order('-created')

    # By multiple tags.
    Question.all().filter('tag', 'foo').filter('tag', 'bar').order('-created')
    """
    filters = [('tags', t) for tag in tags]
    orders = ['-created']
    return question_pager(cursor=cursor, filters=filters, orders=orders,
        limit=limit)


def question_pager_by_newest(cursor=None, limit=20):
    """
    # By newest.
    Question.all().order('-created')
    """
    orders = ['-created']
    return question_pager(cursor=cursor, orders=orders, limit=limit)


def question_pager_by_recent_activity(cursor=None, limit=20):
    """
    # By recent activity.
    Question.all().order('-updated')
    """
    orders = ['-updated']
    return question_pager(cursor=cursor, orders=orders, limit=limit)


def question_pager_by_hotness(cursor=None, limit=20):
    """
    # By hotness.
    Question.all().order('-hotness')
    """
    orders = ['-hotness']
    return question_pager(cursor=cursor, orders=orders, limit=limit)
